package guard

import (
	"net/http"
	"net"
	"sync"
	"os"
	"fmt"
	"log"
	"syscall"
	"os/signal"
	"errors"
	"os/exec"
	"strings"
)

var(
	syncStatus sync.RWMutex
	serversForked bool
	runningServers = make(map[string]*vesyncServer)
	socketPtrOffsetMap = make(map[string]uint)
	socketOrder string
	runningServersOrder = []string{}
)

type vesyncServer struct{
	http.Server

	lock sync.WaitGroup
	VesyncListener net.Listener
	sigChan chan os.Signal
	SignalHooks map[os.Signal][]func()
	isNewServer bool
}

type vesyncListener struct {
	net.Listener
	server *vesyncServer
}

func NewServer(add string, handler http.Handler)(srv *vesyncServer){
	syncStatus.Lock()
	defer syncStatus.Unlock()

	isForkServer := os.Getenv("FORK_VESYNC_SERVER") != ""
	log.Println("env FORK_VESYNC_SERVER is :", isForkServer)
	socketOrder = os.Getenv("VESYNC_SOCKET_ORDER")

	if len(socketOrder) > 0 {
		for i, addr := range strings.Split(socketOrder, ",") {
			socketPtrOffsetMap[addr] = uint(i)
		}
	} else {
		socketPtrOffsetMap[add] = uint(len(runningServersOrder))
	}

	srv = &vesyncServer{
		lock:sync.WaitGroup{},
		sigChan:make(chan os.Signal),
		isNewServer:isForkServer,

		SignalHooks:map[os.Signal][]func(){
			syscall.SIGHUP:[]func(){},
		},
	}

	srv.Server.Addr = add
	srv.Server.Handler = handler

	runningServers[add] = srv
	runningServersOrder = append(runningServersOrder, add)

	return
}

func ListenAndServe(add string, handler http.Handler) error {
	server := NewServer(add, handler)
	return server.ListenAndServe()
}

func (srv *vesyncServer) ListenAndServe()(err error){
	add := srv.Addr
	go srv.SignalHook()

	log.Println("var isNewserver is :", srv.isNewServer)
	if srv.isNewServer {
//		syscall.Kill(syscall.Getppid(), syscall.SIGTERM)
		log.Println("will kill :", syscall.Getppid())
	}

	l, err := srv.getListener(add)
	if err != nil {
		log.Println(err)
		return
	}
	log.Println("get listener success!")
	srv.VesyncListener = convert2VesyncListener(l, srv)



	log.Println("get vesync listener success!")
	return srv.Serve()
}

func (srv *vesyncServer)SignalHook(){
	signal.Notify(srv.sigChan, syscall.SIGHUP)
	for{
		sig := <-srv.sigChan
		log.Println("receive signal :", sig)

		switch sig {
			default:
				log.Println("get sig", sig)
			case syscall.SIGHUP:
				log.Println("get sighup")
			err := srv.fork()
			if err != nil{
				log.Println("fock failed.", err)
			}
		}
	}
}

func (srv *vesyncServer) fork()(err error){
	syncStatus.Lock()
	defer syncStatus.Unlock()

	if serversForked{
		return errors.New("another process already forked, ignore this one")
	}

	serversForked = true

	var files = make([]*os.File, len(runningServers))
	var orderArgs = make([]string, len(runningServers))

	//for _, srvPtr := range runningServers {
	//	// introspect.PrintTypeDump(srvPtr.EndlessListener)
	//	switch srvPtr.VesyncListener.(type) {
	//	case *vesyncServer:
	//		// normal listener
	//		files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.VesyncListener.(*vesyncListener).File()
	//	default:
	//		// tls listener
	//		//files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.tlsInnerListener.File()
	//	}
	//	orderArgs[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.Server.Addr
	//}
	for _,srvPtr := range runningServers{
		files[socketPtrOffsetMap[srvPtr.Server.Addr]] = srvPtr.VesyncListener.(*vesyncListener).File()
	}

	env := append(
		os.Environ(),
		"FORK_VESYNC_SERVER=1",
	)

	if len(runningServers) > 1 {
		env = append(env , fmt.Sprintf(`VESYNC_SOCKET_ORDER=%s`, strings.Join(orderArgs, ",")))
	}

	path := os.Args[0]
	var args []string
	if len(os.Args) > 1 {
		args = os.Args[1:]
	}

	//cmd := exec.Command("echo",path)
	/*cmd := exec.Command(path , args...)
	log.Println("excute new command :", path, args)

	cmd.Stdin = strings.NewReader("some input")
	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Env = env
	err = cmd.Start()
	if err != nil {
		log.Fatal(err)
	}
	log.Println("in all caps: ", out.String())*/
	cmd := exec.Command(path, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	cmd.ExtraFiles = files
	cmd.Env = env

	err = cmd.Start()
	return
}

func convert2VesyncListener(l net.Listener, srv *vesyncServer)(vl *vesyncListener){
	vl = &vesyncListener{
		Listener: l,
		server: srv,
	}
	return
}

func (srv *vesyncServer) getListener(add string)(l net.Listener, err error){
	if srv.isNewServer{
		var ptrOffset uint = 0
		syncStatus.RLock()
		defer syncStatus.RUnlock()
		if len(socketPtrOffsetMap) > 0 {
			ptrOffset = socketPtrOffsetMap[add]
			// log.Println("laddr", laddr, "ptr offset", socketPtrOffsetMap[laddr])
		}

		f := os.NewFile(uintptr(3+ptrOffset), "")
		l, err = net.FileListener(f)
		if err != nil {
			err = fmt.Errorf("net.FileListener error: %v", err)
			return
		}
	}else{
		l, err = net.Listen("tcp", add)
		if err != nil {
			err = fmt.Errorf("net.Listen error: %v", err)
			return
		}
	}
	return
}

func (srv *vesyncServer) Serve()(err error){
	err = srv.Server.Serve(srv.VesyncListener)
	return
}

func (vl *vesyncListener) File() *os.File{
	tl := vl.Listener.(*net.TCPListener)
	fl, _ := tl.File()
	return fl
}